/*
 * Copyright (C) 2010 The Android Open Source Project 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */

/*
 * Sources:
 * http://stackoverflow.com/questions/4847858/how-can-i-get-the-exact-screen-size-of-the-device-through-code
 * http://stackoverflow.com/questions/5948546/close-all-activities-from-the-first-activity-in-an-android-app
 */

package com.gameoflife;

import android.content.Context;
import android.graphics.Point;
import android.util.DisplayMetrics;
import android.view.WindowManager;

/**
 *  Control and update the logic of the game. 
 */
public class Life {
	
	/** General size of each cell.*/
	public static final int CELL_CONSTANT = 8;
	
	/** Actual screen size of each cell. */
    public int cellSize;
    
    /** Amount of columns. */
    public int width;
    
    /** Amount of rows. */
    public int height;

    /** Holds the state of each cell on the board. */
    private int[][] _lifeGrid = new int[height][width];
    
    /** Holds the number of iterations each cell has survived. */
    private int[][] _colorGrid = new int[height][width];
    
    /** Holds cells that have been touched. */
    private int[][] _touchedGrid = new int[height][width];

    private Context _context;
    
    /** 
     * Initialize the logic of the game - initialize arrays and get the correct sizes for game 
     * elements. 
     */
    public Life(Context context) {
    	this._context = context;
        WindowManager mWinMgr = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Point displaySize = new Point();
        mWinMgr.getDefaultDisplay().getSize(displaySize);
        float deviceWidth = (float)displaySize.x;
		float deviceHeight = (float)displaySize.y;
		
		DisplayMetrics metrics = new DisplayMetrics();
		mWinMgr.getDefaultDisplay().getMetrics(metrics);
		cellSize = (int)(metrics.density * CELL_CONSTANT);
        
        width = (int)(deviceWidth) / cellSize;
        height = (int)(deviceHeight) / cellSize;
        _lifeGrid = new int[height][width];
        _colorGrid = new int[height][width];
        _touchedGrid = new int[height][width];
        initializeGrid();
    }

    public int[][] getGrid() {
        return _lifeGrid;
    }
    
    public int[][] getColorGrid() {
    	return _colorGrid;
    }

    /** Set a default state for the grid. */
    public void initializeGrid() {
        resetGrid(_lifeGrid);

        _lifeGrid[8][(width / 2) - 1] = 1;
        _lifeGrid[8][(width / 2) + 1] = 1;
        _lifeGrid[9][(width / 2) - 1] = 1;
        _lifeGrid[9][(width / 2) + 1] = 1;
        _lifeGrid[10][(width / 2) - 1] = 1;
        _lifeGrid[10][(width / 2)] = 1;
        _lifeGrid[10][(width / 2) + 1] = 1;
    }

    /** Calculate the next generation of cells by looking at their neighbors. */
    public void generateNextGeneration() {
        int neighbours;
        int minimum = Integer.parseInt(PreferencesActivity
                .getMinimumVariable(this._context));
        int maximum = Integer.parseInt(PreferencesActivity
                .getMaximumVariable(this._context));
        int spawn = Integer.parseInt(PreferencesActivity
                .getSpawnVariable(this._context));

        int[][] nextGenerationLifeGrid = new int[height][width];

        for (int h = 0; h < height; h++) {
            for (int w = 0; w < width; w++) {
            	// Handle touched cells
            	if (_touchedGrid[h][w] == 1){
            		if (_lifeGrid[h][w] == 0){
            			nextGenerationLifeGrid[h][w] = 1;
            		}
            		else{
            			nextGenerationLifeGrid[h][w] = 0;
            		}
            	}
            	else{
            		neighbours = calculateNeighbours(h, w);

                    if (_lifeGrid[h][w] != 0) {
                        if ((neighbours >= minimum) && (neighbours <= maximum)) {
                            nextGenerationLifeGrid[h][w] = neighbours;
                        }
                    } else {
                        if (neighbours == spawn) {
                            nextGenerationLifeGrid[h][w] = spawn;
                        }
                    }
            	}
            }
        }
        copyGrid(nextGenerationLifeGrid, _lifeGrid);
        clearTouchGrid();
    }

    /** Set all the values in an array to 0. */
    private void resetGrid(int[][] grid) {
        for (int h = 0; h < height; h++) {
            for (int w = 0; w < width; w++) {
                grid[h][w] = 0;
            }
        }
    }

    /** Get the amount of neighbors next to a cell. */
    private int calculateNeighbours(int y, int x) {
        int total = (_lifeGrid[y][x] != 0) ? -1 : 0;
        for (int h = -1; h <= +1; h++) {
            for (int w = -1; w <= +1; w++) {
                if (_lifeGrid[(height + (y + h)) % height][(width + (x + w))
                        % width] != 0) {
                    total++;
                }
            }
        }
        return total;
    }

    /** Copy the new grid to the current grid. */
    private void copyGrid(int[][] source, int[][] destination) {
        for (int h = 0; h < height; h++) {
            for (int w = 0; w < width; w++) {
            	if (destination[h][w] != 0 && source[h][w] != 0){
            		_colorGrid[h][w]++;
                }
                destination[h][w] = source[h][w];
                if (destination[h][w] == 0){
                	_colorGrid[h][w] = 0;
                }
            }
        }
    }
    
    /** Set the touch flag for a cell to true for this update. */
    public void touchCell(float x, float y){
    	int cellX = (int)(x / cellSize);
    	int cellY = (int)(y / cellSize);
    	if (cellX > -1 && cellY > -1 && cellY < height && cellX < width){
    		_touchedGrid[cellY][cellX] = 1;
    	}
    }
     /** Reset touch flags. */
    private void clearTouchGrid(){
    	for(int y = 0; y < height; y++){
    		for (int x = 0; x < width; x++){
    			_touchedGrid[y][x] = 0;
    		}
    	}
    }
}